/*
 * Copyright 2009 Original Author(s)
 * 
 * This file is part of Kommando
 * 
 * Kommando is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Kommando is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Kommando.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.kommando.skin.bezel.util;

import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Transparency;
import java.awt.image.BufferedImage;

import javax.swing.JComponent;

/**
 * Softclipping code taken from http://weblogs.java.net/blog/campbell/archive/2006/07/java_2d_tricker.html
 * 
 * @author Peter De Bruycker
 */
public abstract class SoftClipDecorator extends JComponent {
    private static final long serialVersionUID = 1L;

    public SoftClipDecorator(JComponent delegate) {
        setLayout(new BorderLayout());
        add(delegate);
    }

    @Override
    public void paint(Graphics g) {
        Graphics2D graphics = (Graphics2D) g.create();

        Shape clip = getClip();
        int x = clip.getBounds().x;
        int y = clip.getBounds().y;
        int width = clip.getBounds().width;
        int height = clip.getBounds().height;

        // Create a translucent intermediate image in which we can perform
        // the soft clipping
        GraphicsConfiguration gc = graphics.getDeviceConfiguration();
        BufferedImage background = gc.createCompatibleImage(width, height, Transparency.TRANSLUCENT);
        Graphics2D imageGraphics = background.createGraphics();

        // Clear the image so all pixels have zero alpha
        imageGraphics.setComposite(AlphaComposite.Clear);
        imageGraphics.fillRect(x, y, width, height);

        // Render our clip shape into the image. Note that we enable
        // antialiasing to achieve the soft clipping effect. Try
        // commenting out the line that enables antialiasing, and
        // you will see that you end up with the usual hard clipping.
        imageGraphics.setComposite(AlphaComposite.Src);
        imageGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        imageGraphics.setColor(Color.WHITE);
        imageGraphics.fill(clip);

        // Here's the trick... We use SrcAtop, which effectively uses the
        // alpha value as a coverage value for each pixel stored in the
        // destination. For the areas outside our clip shape, the destination
        // alpha will be zero, so nothing is rendered in those areas. For
        // the areas inside our clip shape, the destination alpha will be fully
        // opaque, so the full color is rendered. At the edges, the original
        // antialiasing is carried over to give us the desired soft clipping
        // effect.
        imageGraphics.setComposite(AlphaComposite.SrcAtop);

        doPaint(imageGraphics);

        graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_OVER, 0.85f));

        // Copy our intermediate image to the screen
        doPaint(graphics, background, x, y);

        graphics.dispose();
    }

    /**
     * Image is softclipped. Can be overridden to achieve translucency, ...
     */
    protected void doPaint(Graphics2D graphics, BufferedImage image, int x, int y) {
        graphics.drawImage(image, x, y, null);
    }

    protected abstract Shape getClip();

    protected abstract void doPaint(Graphics2D graphics);
}
